package com.core.cloudcommon.security;

import com.alibaba.fastjson.JSON;
import com.core.cloudcommon.CommonCustomException;
import com.core.cloudcommon.redis.service.CloudRedisTemplateService;
import com.core.cloudcommon.redis.vo.CloudRedisVO;
import com.core.cloudcommon.enums.CloudSystemLoginResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author =====Lqj=====
 * @className IpAccessControlManager
 * @description IP访问控制管理器
 * @create 2024/12/16 14:00
 **/
@Slf4j
@Component
public class IpAccessControlManager {

    @Autowired
    private CloudRedisTemplateService cloudRedisTemplateService;

    // Redis Key前缀
    private static final String IP_WHITELIST_KEY = "ip_whitelist";
    private static final String IP_BLACKLIST_KEY = "ip_blacklist";
    private static final String IP_TEMP_BLOCK_KEY = "ip_temp_block:";

    // 配置参数
    @Value("${security.ip.whitelist.enabled:false}")
    private boolean whitelistEnabled;

    @Value("${security.ip.blacklist.enabled:true}")
    private boolean blacklistEnabled;

    @Value("${security.ip.auto-block.enabled:true}")
    private boolean autoBlockEnabled;

    @Value("${security.ip.auto-block.threshold:10}")
    private int autoBlockThreshold;

    @Value("${security.ip.auto-block.duration:3600}")
    private long autoBlockDuration; // 秒

    // 内存缓存，提高性能
    private final Map<String, Boolean> whitelistCache = new ConcurrentHashMap<>();
    private final Map<String, Boolean> blacklistCache = new ConcurrentHashMap<>();
    private volatile long lastCacheUpdate = 0;
    private static final long CACHE_EXPIRE_TIME = 5 * 60 * 1000; // 5分钟

    /**
     * 检查IP是否允许访问
     * @param ip IP地址
     * @return true-允许，false-拒绝
     * @throws CommonCustomException IP被拒绝时抛出异常
     */
    public boolean checkIpAccess(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return true;
        }

        // 更新缓存
        updateCacheIfNeeded();

        // 检查临时封禁
        if (isTemporaryBlocked(ip)) {
            throw new CommonCustomException(CloudSystemLoginResultCode.CODE200020.getCode(), 
                "IP地址已被临时封禁，请稍后重试");
        }

        // 检查黑名单
        if (blacklistEnabled && isInBlacklist(ip)) {
            throw new CommonCustomException(CloudSystemLoginResultCode.CODE200020.getCode(), 
                "IP地址已被加入黑名单");
        }

        // 检查白名单
        if (whitelistEnabled) {
            if (!isInWhitelist(ip)) {
                throw new CommonCustomException(CloudSystemLoginResultCode.CODE200020.getCode(), 
                    "IP地址不在白名单中");
            }
        }

        return true;
    }

    /**
     * 添加IP到白名单
     * @param ip IP地址
     * @param remark 备注
     */
    public void addToWhitelist(String ip, String remark) {
        Set<String> whitelist = getWhitelist();
        whitelist.add(ip);
        saveWhitelist(whitelist);
        whitelistCache.put(ip, true);
        
        log.info("IP {} 已添加到白名单，备注: {}", ip, remark);
    }

    /**
     * 从白名单移除IP
     * @param ip IP地址
     */
    public void removeFromWhitelist(String ip) {
        Set<String> whitelist = getWhitelist();
        whitelist.remove(ip);
        saveWhitelist(whitelist);
        whitelistCache.remove(ip);
        
        log.info("IP {} 已从白名单移除", ip);
    }

    /**
     * 添加IP到黑名单
     * @param ip IP地址
     * @param remark 备注
     */
    public void addToBlacklist(String ip, String remark) {
        Set<String> blacklist = getBlacklist();
        blacklist.add(ip);
        saveBlacklist(blacklist);
        blacklistCache.put(ip, true);
        
        log.info("IP {} 已添加到黑名单，备注: {}", ip, remark);
    }

    /**
     * 从黑名单移除IP
     * @param ip IP地址
     */
    public void removeFromBlacklist(String ip) {
        Set<String> blacklist = getBlacklist();
        blacklist.remove(ip);
        saveBlacklist(blacklist);
        blacklistCache.remove(ip);
        
        log.info("IP {} 已从黑名单移除", ip);
    }

    /**
     * 临时封禁IP
     * @param ip IP地址
     * @param duration 封禁时长（秒）
     * @param reason 封禁原因
     */
    public void temporaryBlockIp(String ip, long duration, String reason) {
        String key = IP_TEMP_BLOCK_KEY + ip;
        Map<String, Object> blockInfo = new HashMap<>();
        blockInfo.put("ip", ip);
        blockInfo.put("reason", reason);
        blockInfo.put("blockTime", new Date());
        blockInfo.put("duration", duration);
        
        cloudRedisTemplateService.set(new CloudRedisVO(key, JSON.toJSONString(blockInfo), duration));
        
        log.warn("IP {} 已被临时封禁 {} 秒，原因: {}", ip, duration, reason);
    }

    /**
     * 自动封禁IP（基于失败次数）
     * @param ip IP地址
     * @param failCount 失败次数
     */
    public void autoBlockIpIfNeeded(String ip, int failCount) {
        if (!autoBlockEnabled) {
            return;
        }

        if (failCount >= autoBlockThreshold) {
            temporaryBlockIp(ip, autoBlockDuration, 
                String.format("自动封禁：失败次数达到%d次", failCount));
        }
    }

    /**
     * 解除IP临时封禁
     * @param ip IP地址
     */
    public void unblockIp(String ip) {
        String key = IP_TEMP_BLOCK_KEY + ip;
        cloudRedisTemplateService.del(key);
        
        log.info("IP {} 临时封禁已解除", ip);
    }

    /**
     * 检查IP是否在白名单中
     */
    private boolean isInWhitelist(String ip) {
        if (whitelistCache.containsKey(ip)) {
            return whitelistCache.get(ip);
        }

        Set<String> whitelist = getWhitelist();
        boolean inWhitelist = whitelist.contains(ip) || isIpInRange(ip, whitelist);
        whitelistCache.put(ip, inWhitelist);
        
        return inWhitelist;
    }

    /**
     * 检查IP是否在黑名单中
     */
    private boolean isInBlacklist(String ip) {
        if (blacklistCache.containsKey(ip)) {
            return blacklistCache.get(ip);
        }

        Set<String> blacklist = getBlacklist();
        boolean inBlacklist = blacklist.contains(ip) || isIpInRange(ip, blacklist);
        blacklistCache.put(ip, inBlacklist);
        
        return inBlacklist;
    }

    /**
     * 检查IP是否被临时封禁
     */
    private boolean isTemporaryBlocked(String ip) {
        String key = IP_TEMP_BLOCK_KEY + ip;
        Object blockInfo = cloudRedisTemplateService.get(new CloudRedisVO(key));
        return blockInfo != null;
    }

    /**
     * 检查IP是否在指定范围内
     * 支持CIDR格式，如：192.168.1.0/24
     */
    private boolean isIpInRange(String ip, Set<String> ipRanges) {
        for (String range : ipRanges) {
            if (range.contains("/")) {
                // CIDR格式
                if (isIpInCidr(ip, range)) {
                    return true;
                }
            } else if (range.contains("*")) {
                // 通配符格式，如：192.168.1.*
                if (isIpMatchWildcard(ip, range)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 检查IP是否在CIDR范围内
     */
    private boolean isIpInCidr(String ip, String cidr) {
        try {
            String[] parts = cidr.split("/");
            String networkIp = parts[0];
            int prefixLength = Integer.parseInt(parts[1]);
            
            long ipLong = ipToLong(ip);
            long networkLong = ipToLong(networkIp);
            long mask = (0xFFFFFFFFL << (32 - prefixLength)) & 0xFFFFFFFFL;
            
            return (ipLong & mask) == (networkLong & mask);
        } catch (Exception e) {
            log.error("CIDR格式错误: {}", cidr);
            return false;
        }
    }

    /**
     * 检查IP是否匹配通配符
     */
    private boolean isIpMatchWildcard(String ip, String pattern) {
        String regex = pattern.replace(".", "\\.")
                             .replace("*", "\\d+");
        return ip.matches(regex);
    }

    /**
     * IP地址转换为长整型
     */
    private long ipToLong(String ip) {
        String[] parts = ip.split("\\.");
        long result = 0;
        for (int i = 0; i < 4; i++) {
            result = result * 256 + Integer.parseInt(parts[i]);
        }
        return result;
    }

    /**
     * 获取白名单
     */
    private Set<String> getWhitelist() {
        Object obj = cloudRedisTemplateService.get(new CloudRedisVO(IP_WHITELIST_KEY));
        if (obj == null) {
            return new HashSet<>();
        }
        try {
            return new HashSet<>(JSON.parseArray(obj.toString(), String.class));
        } catch (Exception e) {
            log.error("解析白名单失败: {}", e.getMessage());
            return new HashSet<>();
        }
    }

    /**
     * 保存白名单
     */
    private void saveWhitelist(Set<String> whitelist) {
        cloudRedisTemplateService.set(new CloudRedisVO(IP_WHITELIST_KEY, JSON.toJSONString(whitelist), -1L));
    }

    /**
     * 获取黑名单
     */
    private Set<String> getBlacklist() {
        Object obj = cloudRedisTemplateService.get(new CloudRedisVO(IP_BLACKLIST_KEY));
        if (obj == null) {
            return new HashSet<>();
        }
        try {
            return new HashSet<>(JSON.parseArray(obj.toString(), String.class));
        } catch (Exception e) {
            log.error("解析黑名单失败: {}", e.getMessage());
            return new HashSet<>();
        }
    }

    /**
     * 保存黑名单
     */
    private void saveBlacklist(Set<String> blacklist) {
        cloudRedisTemplateService.set(new CloudRedisVO(IP_BLACKLIST_KEY, JSON.toJSONString(blacklist), -1L));
    }

    /**
     * 更新缓存（如果需要）
     */
    private void updateCacheIfNeeded() {
        long now = System.currentTimeMillis();
        if (now - lastCacheUpdate > CACHE_EXPIRE_TIME) {
            whitelistCache.clear();
            blacklistCache.clear();
            lastCacheUpdate = now;
        }
    }

    /**
     * 获取IP封禁信息
     * @param ip IP地址
     * @return 封禁信息
     */
    public Map<String, Object> getIpBlockInfo(String ip) {
        String key = IP_TEMP_BLOCK_KEY + ip;
        Object blockInfo = cloudRedisTemplateService.get(new CloudRedisVO(key));
        
        if (blockInfo != null) {
            try {
                return JSON.parseObject(blockInfo.toString(), Map.class);
            } catch (Exception e) {
                log.error("解析IP封禁信息失败: {}", e.getMessage());
            }
        }
        
        return null;
    }

    /**
     * 获取所有白名单IP
     */
    public Set<String> getAllWhitelistIps() {
        return getWhitelist();
    }

    /**
     * 获取所有黑名单IP
     */
    public Set<String> getAllBlacklistIps() {
        return getBlacklist();
    }
}
